package com.seanzx.client;

import com.seanzx.common.NetSerializer;
import com.seanzx.common.StringUtils;

import java.io.*;
import java.net.Socket;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;

public class SeanDBClient {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Please input SeanDB Server ip:");
        try (
            Socket socket = new Socket(scanner.nextLine(), 13306);
            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))
        ) {
            System.out.println("connect success.");
            while (true) {
                String str = read(scanner);
                if (str.equals("exit")) {
                    System.out.println("bye!");
                    socket.close();
                    return;
                }
                if (str.startsWith("use file ")) {
                    try (BufferedReader reader = new BufferedReader(new FileReader(str.substring(9, str.length() - 1)))) {
                        String sql;
                        StringBuilder builder = new StringBuilder();
                        while ((sql = reader.readLine()) != null) {
                            System.out.println("SeanDB >>");
                            System.out.println(sql);
                            builder.append(" ").append(sql);
                            if (sql.endsWith(";")) {
                                ioOperate(pw, br, builder.toString());
                                builder = new StringBuilder();
                            }
                        }
                    }
                    continue;
                }
                ioOperate(pw, br, str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            scanner.close();
        }
    }

    private static void ioOperate(PrintWriter pw, BufferedReader br, String str) throws IOException {
        boolean isEgo = sendRequest(pw, str);

        NetSerializer.NetRes res = waitForResponse(br);

        printResult(res, isEgo);
    }

    private static boolean sendRequest(PrintWriter pw, String str) {
        boolean isEgo = false;
        if (str.endsWith("\\G;")) {
            isEgo = true;
            pw.write(str.substring(0, str.length() - 3) + "\n");
        } else {
            pw.write(str.substring(0, str.length() - 1) + "\n");
        }
        pw.flush();
        return isEgo;
    }

    private static NetSerializer.NetRes waitForResponse(BufferedReader br) throws IOException {
        StringBuilder resultBuilder = new StringBuilder();
        String info;
        do {
            info = br.readLine();
            resultBuilder.append(info);
        } while (!info.endsWith("\002"));

        NetSerializer.NetRes res = NetSerializer.deserialize(resultBuilder.substring(0, resultBuilder.length() - 1));
        return res;
    }

    private static void printResult(NetSerializer.NetRes res, boolean isEgo) {
        switch (res.getType()) {
            case NetSerializer.TYPE_DATA:
                if (isEgo) {
                    printRowsEgo(res);
                } else {
                    printRows(res);
                }
                System.out.println();
                System.out.print("selected " + res.getRows() + " rows,");
                break;
            case NetSerializer.TYPE_ROWS:
                System.out.print("affected " + res.getRows() + " rows,");
                break;
            default:
                System.out.println("[" + res.getResultCode() + "]" + res.getResultMessage());
                break;
        }
        System.out.println("execute " + res.getExecuteTime() + " ms");
    }

    private static void printRows(NetSerializer.NetRes res) {
        for (List<String> row : res.getResults()) {
            for (String col : row) {
                System.out.print(col + "\t");
            }
            System.out.println();
        }
    }
    
    private static void printRowsEgo(NetSerializer.NetRes res) {
        List<String> headers = res.getResults().get(0);
        int length = headers.stream().max(Comparator.comparingInt(String::length)).get().length();
        headers = headers.stream().map(h -> StringUtils.lpad(h, length + 2, ' ')).collect(Collectors.toList());

        for (int i = 1; i < res.getResults().size(); i++) {
            System.out.println("************************* " + i + " row *************************");
            List<String> cols = res.getResults().get(i);
            for (int j = 0; j < cols.size(); j++) {
                System.out.println("\t" + headers.get(j) + ": " + cols.get(j));
            }
        }
    }

    private static String read(Scanner scanner) {
        String str;
        StringBuilder builder = new StringBuilder();
        System.out.println("SeanDB >>");
        while ((str = scanner.nextLine()) != null) {
            builder.append(str);
            if (str.endsWith(";") || str.equals("exit")) {
                break;
            }
            builder.append(" ");
        }
        return builder.toString();
    }
}
